//  Solution.java
//
//  Author:
//       Antonio J. Nebro <antonio@lcc.uma.es>
//       Juan J. Durillo <durillo@lcc.uma.es>
//
//  Description: 
// 
//  Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

package hidra.jmetal.core;

import java.io.Serializable;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import jmetal.encodings.variable.Binary;
import jmetal.util.Configuration.*;

/**
 * Class representing a solution for a problem.
 */
public class Solution implements Serializable {  
	/**
	 * Stores the problem 
	 */
	Problem problem_ ;

	/**
	 * Stores the type of the variable
	 */	
	private SolutionType type_ ; 

	/**
	 * Stores the decision variables of the solution.
	 */
	private Variable[] variable_ ;

	/**
	 * Stores the objectives values of the solution.
	 */
	private double [] objective_ ;

	/**
	 * Stores the number of objective values of the solution
	 */
	private int numberOfObjectives_ ;

	/**
	 * Stores the so called fitness value. Used in some metaheuristics
	 */
	private double fitness_ ;

	/**
	 * Used in algorithm AbYSS, this field is intended to be used to know
	 * when a <code>Solution</code> is marked.
	 */
	private boolean marked_ ;

	/**
	 * Stores the so called rank of the solution. Used in NSGA-II
	 */
	private int rank_ ;

	/**
	 * Stores the overall constraint violation of the solution.
	 */
	private double  overallConstraintViolation_ ;

	/**
	 * Stores the number of constraints violated by the solution.
	 */
	private int  numberOfViolatedConstraints_ ;

	/**
	 * This field is intended to be used to know the location of
	 * a solution into a <code>SolutionSet</code>. Used in MOCell
	 */
	private int location_ ;

	/**
	 * Stores the distance to his k-nearest neighbor into a 
	 * <code>SolutionSet</code>. Used in SPEA2.
	 */
	private double kDistance_ ; 

	/**
	 * Stores the crowding distance of the the solution in a 
	 * <code>SolutionSet</code>. Used in NSGA-II.
	 */
	private double crowdingDistance_ ; 

	/**
	 * Stores the diversity factor of the the solution in a 
	 * <code>SolutionSet</code>. Used in MOPSO-DFR.
	 */
	private double diversityFactor_ ; 

	/**
	 * Stores the diversity factor v2 of the the solution in a 
	 * <code>SolutionSet</code>. Used in MOPSO-DFR.
	 */
	private double diversityFactorV2_ ; 

	/**
	 * Stores the distance between this solution and a <code>SolutionSet</code>.
	 * Used in AbySS.
	 */
	private double distanceToSolutionSet_ ;

	/**
	 * Stores the average K distance between this solution and a <code>SolutionSet</code>.
	 */
	private double densityEstimator_ ;

	/**
	 * Stores the solution index.
	 */
	private int label_;

	/**
	 * Constructor.
	 */
	public Solution() {        
		problem_                      = null  ;
		marked_                       = false ;
		overallConstraintViolation_   = 0.0   ;
		numberOfViolatedConstraints_  = 0     ;  
		type_                         = null ;
		variable_                     = null ;
		objective_                    = null ;
	} // Solution

	/**
	 * Constructor
	 * @param numberOfObjectives Number of objectives of the solution
	 * 
	 * This constructor is used mainly to read objective values from a file to
	 * variables of a SolutionSet to apply quality indicators
	 */
	public Solution(int numberOfObjectives) {
		numberOfObjectives_ = numberOfObjectives;
		objective_          = new double[numberOfObjectives];
	}

	/** 
	 * Constructor.
	 * @param problem The problem to solve
	 * @throws ClassNotFoundException 
	 */
	public Solution(Problem problem) throws ClassNotFoundException{
		problem_ = problem ; 
		type_ = problem.getSolutionType() ;
		numberOfObjectives_ = problem.getNumberOfObjectives() ;
		objective_          = new double[numberOfObjectives_] ;

		// Setting initial values
		fitness_              = 0.0 ;
		kDistance_            = 0.0 ;
		crowdingDistance_     = 0.0 ;   
		diversityFactor_	  = 0.0 ;
		diversityFactorV2_	  = 0.0 ;

		distanceToSolutionSet_ = Double.POSITIVE_INFINITY ;
		//<-

		//variable_ = problem.solutionType_.createVariables() ; 
		variable_ = type_.createVariables() ; 
	} // Solution

	static public Solution getNewSolution(Problem problem) throws ClassNotFoundException {
		return new Solution(problem) ;
	}

	/** 
	 * Constructor
	 * @param problem The problem to solve
	 */
	public Solution(Problem problem, Variable [] variables){
		problem_ = problem ;
		type_ = problem.getSolutionType() ;
		numberOfObjectives_ = problem.getNumberOfObjectives() ;
		objective_          = new double[numberOfObjectives_] ;

		// Setting initial values
		fitness_              = 0.0 ;
		kDistance_            = 0.0 ;
		crowdingDistance_     = 0.0 ;        
		distanceToSolutionSet_ = Double.POSITIVE_INFINITY ;
		//<-

		variable_ = variables ;
	} // Constructor

	/** 
	 * Copy constructor.
	 * @param solution Solution to copy.
	 */    
	public Solution(Solution solution) {            
		problem_ = solution.problem_ ;
		type_ = solution.type_;

		numberOfObjectives_ = solution.numberOfObjectives();
		objective_ = new double[numberOfObjectives_];
		for (int i = 0; i < objective_.length;i++) {
			objective_[i] = solution.getObjective(i);
		} // for
		//<-

		variable_ = type_.copyVariables(solution.variable_) ;
		overallConstraintViolation_  = solution.getOverallConstraintViolation();
		numberOfViolatedConstraints_ = solution.getNumberOfViolatedConstraint();
		distanceToSolutionSet_ = solution.getDistanceToSolutionSet();
		crowdingDistance_     = solution.getCrowdingDistance();
		diversityFactor_ 	  = solution.getDiversityFactor();
		kDistance_            = solution.getKDistance();                
		fitness_              = solution.getFitness();
		marked_               = solution.isMarked();
		rank_                 = solution.getRank();
		location_             = solution.getLocation();
		label_				  = solution.getLabel();
	} // Solution

	/**
	 * Sets the distance between this solution and a <code>SolutionSet</code>.
	 * The value is stored in <code>distanceToSolutionSet_</code>.
	 * @param distance The distance to a solutionSet.
	 */
	public void setDistanceToSolutionSet(double distance){
		distanceToSolutionSet_ = distance;
	} // SetDistanceToSolutionSet

	/**
	 * Gets the distance from the solution to a <code>SolutionSet</code>. 
	 * <b> REQUIRE </b>: this method has to be invoked after calling 
	 * <code>setDistanceToPopulation</code>.
	 * @return the distance to a specific solutionSet.
	 */
	public double getDistanceToSolutionSet(){
		return distanceToSolutionSet_;
	} // getDistanceToSolutionSet


	/** 
	 * Sets the distance between the solution and its k-nearest neighbor in 
	 * a <code>SolutionSet</code>. The value is stored in <code>kDistance_</code>.
	 * @param distance The distance to the k-nearest neighbor.
	 */
	public void setKDistance(double distance){
		kDistance_ = distance;
	} // setKDistance

	/** 
	 * Gets the distance from the solution to his k-nearest nighbor in a 
	 * <code>SolutionSet</code>. Returns the value stored in
	 * <code>kDistance_</code>. <b> REQUIRE </b>: this method has to be invoked 
	 * after calling <code>setKDistance</code>.
	 * @return the distance to k-nearest neighbor.
	 */
	public double getKDistance(){
		return kDistance_;
	} // getKDistance

	/**
	 * Sets the crowding distance of a solution in a <code>SolutionSet</code>.
	 * The value is stored in <code>crowdingDistance_</code>.
	 * @param distance The crowding distance of the solution.
	 */  
	public void setCrowdingDistance(double distance){
		crowdingDistance_ = distance;
	} // setCrowdingDistance


	/** 
	 * Gets the crowding distance of the solution into a <code>SolutionSet</code>.
	 * Returns the value stored in <code>crowdingDistance_</code>.
	 * <b> REQUIRE </b>: this method has to be invoked after calling 
	 * <code>setCrowdingDistance</code>.
	 * @return the distance crowding distance of the solution.
	 */
	public double getCrowdingDistance(){
		return crowdingDistance_;
	} // getCrowdingDistance

	/**
	 * Sets the fitness of a solution.
	 * The value is stored in <code>fitness_</code>.
	 * @param fitness The fitness of the solution.
	 */
	public void setFitness(double fitness) {
		fitness_ = fitness;
	} // setFitness

	/**
	 * Gets the fitness of the solution.
	 * Returns the value of stored in the variable <code>fitness_</code>.
	 * <b> REQUIRE </b>: This method has to be invoked after calling 
	 * <code>setFitness()</code>.
	 * @return the fitness.
	 */
	public double getFitness() {
		return fitness_;
	} // getFitness

	/**
	 * Sets the value of the i-th objective.
	 * @param i The number identifying the objective.
	 * @param value The value to be stored.
	 */
	public void setObjective(int i, double value) {
		objective_[i] = value;
	} // setObjective

	/**
	 * Returns the value of the i-th objective.
	 * @param i The value of the objective.
	 */
	public double getObjective(int i) {
		return objective_[i];
	} // getObjective

	/**
	 * Returns the number of objectives.
	 * @return The number of objectives.
	 */
	public int numberOfObjectives() {
		if (objective_ == null)
			return 0 ;
		else
			return numberOfObjectives_;
	} // numberOfObjectives

	/**  
	 * Returns the number of decision variables of the solution.
	 * @return The number of decision variables.
	 */
	public int numberOfVariables() {
		return problem_.getNumberOfVariables() ;
	} // numberOfVariables

	/** 
	 * Returns a string representing the solution.
	 * @return The string.
	 */
	public String toString() {
		String aux="";
		for (int i = 0; i < this.numberOfObjectives_; i++){
			aux = aux + this.getObjective(i) + " ";
		}

		return aux;
	} // toString

	/**
	 * Returns the decision variables of the solution.
	 * @return the <code>DecisionVariables</code> object representing the decision
	 * variables of the solution.
	 */
	public Variable[] getDecisionVariables() {
		return variable_ ;
	} // getDecisionVariables

	/**
	 * Sets the decision variables for the solution.
	 * @param decisionVariables The <code>DecisionVariables</code> object 
	 * representing the decision variables of the solution.
	 */
	public void setDecisionVariables(Variable [] variables) {
		variable_ = variables ;
	} // setDecisionVariables

	/**
	 * Indicates if the solution is marked.
	 * @return true if the method <code>marked</code> has been called and, after 
	 * that, the method <code>unmarked</code> hasn't been called. False in other
	 * case.
	 */
	public boolean isMarked() {
		return this.marked_;
	} // isMarked

	/**
	 * Establishes the solution as marked.
	 */
	public void marked() {
		this.marked_ = true;
	} // marked

	/**
	 * Established the solution as unmarked.
	 */
	public void unMarked() {
		this.marked_ = false;
	} // unMarked

	/**  
	 * Sets the rank of a solution. 
	 * @param value The rank of the solution.
	 */
	public void setRank(int value){
		this.rank_ = value;
	} // setRank

	/**
	 * Gets the rank of the solution.
	 * <b> REQUIRE </b>: This method has to be invoked after calling 
	 * <code>setRank()</code>.
	 * @return the rank of the solution.
	 */
	public int getRank(){
		return this.rank_;
	} // getRank

	/**
	 * Sets the overall constraints violated by the solution.
	 * @param value The overall constraints violated by the solution.
	 */
	public void setOverallConstraintViolation(double value) {
		this.overallConstraintViolation_ = value;
	} // setOverallConstraintViolation

	/**
	 * Gets the overall constraint violated by the solution.
	 * <b> REQUIRE </b>: This method has to be invoked after calling 
	 * <code>overallConstraintViolation</code>.
	 * @return the overall constraint violation by the solution.
	 */
	public double getOverallConstraintViolation() {
		return this.overallConstraintViolation_;
	}  //getOverallConstraintViolation


	/**
	 * Sets the number of constraints violated by the solution.
	 * @param value The number of constraints violated by the solution.
	 */
	public void setNumberOfViolatedConstraint(int value) {
		this.numberOfViolatedConstraints_ = value;
	} //setNumberOfViolatedConstraint

	/**
	 * Gets the number of constraint violated by the solution.
	 * <b> REQUIRE </b>: This method has to be invoked after calling
	 * <code>setNumberOfViolatedConstraint</code>.
	 * @return the number of constraints violated by the solution.
	 */
	public int getNumberOfViolatedConstraint() {
		return this.numberOfViolatedConstraints_;
	} // getNumberOfViolatedConstraint

	/**
	 * Sets the location of the solution into a solutionSet. 
	 * @param location The location of the solution.
	 */
	public void setLocation(int location) {
		this.location_ = location;
	} // setLocation

	/**
	 * Gets the location of this solution in a <code>SolutionSet</code>.
	 * <b> REQUIRE </b>: This method has to be invoked after calling
	 * <code>setLocation</code>.
	 * @return the location of the solution into a solutionSet
	 */
	public int getLocation() {
		return this.location_;
	} // getLocation

	/**
	 * Sets the type of the variable. 
	 * @param type The type of the variable.
	 */
	//public void setType(String type) {
	// type_ = Class.forName("") ;
	//} // setType

	/**
	 * Sets the type of the variable. 
	 * @param type The type of the variable.
	 */
	public void setType(SolutionType type) {
		type_ = type ;
	} // setType

	/**
	 * Gets the type of the variable
	 * @return the type of the variable
	 */
	public SolutionType getType() {
		return type_;
	} // getType

	/** 
	 * Returns the aggregative value of the solution
	 * @return The aggregative value.
	 */
	public double getAggregativeValue() {
		double value = 0.0;                
		for (int i = 0; i < numberOfObjectives(); i++){            
			value += getObjective(i);
		}                
		return value;
	} // getAggregativeValue

	/**
	 * Returns the number of bits of the chromosome in case of using a binary
	 * representation
	 * @return The number of bits if the case of binary variables, 0 otherwise
	 */
	public int getNumberOfBits() {
		int bits = 0 ;

		for (int i = 0;  i < variable_.length  ; i++)
			try {
				if ((variable_[i].getVariableType() == Class.forName("jmetal.base.variable.Binary")) ||
						(variable_[i].getVariableType() == Class.forName("jmetal.base.variable.BinaryReal")))
					bits += ((Binary)(variable_[i])).getNumberOfBits() ;
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		return bits ;
	} // getNumberOfBits



	//==============================Add by ELliackin=================================


	private int[] scores_;


	public int[] getScores(){
		return this.scores_;
	}

	public void setScores(int[] scores){
		this.scores_ = scores;
	}


	private List<Solution>  neighbors = new ArrayList<Solution>();
	private double gr;
	private double gcd;
	private double gcpd;


	private int index;
	private double weight;


	public List<Solution> getNeighbors() {
		return neighbors;
	}

	public void setNeighbors(List<Solution> neighbors) {
		this.neighbors = neighbors;
	}

	public double getGR() {
		return gr;
	}

	public void setGR(double gr) {
		this.gr = gr;
	}

	public double getGcd() {
		return gcd;
	}

	public void setGCD(double gcd) {
		this.gcd = gcd;
	}

	public double getGcpd() {
		return gcpd;
	}

	public void setGCPD(double gcpd) {
		this.gcpd = gcpd;
	}

	public double[] getObjectives(){
		return objective_.clone();
	}


	public void setIndex(int index){
		this.index = index;
	}

	public int getIndex(){
		return index;
	}

	public double getWeight() {
		return weight;
	}

	public void setWeight(double weight) {
		this.weight = weight;
	}

	public void setDiversityFactor(double diversityFactor) {
		this.diversityFactor_ = diversityFactor;
	}

	public double getDiversityFactor() {
		return this.diversityFactor_;
	}

	public double getDensityEstimator() {
		return densityEstimator_;
	}

	public void setDensityEstimator(double densityEstimator_) {
		this.densityEstimator_ = densityEstimator_;
	}

	private Solution solution;

	public Solution getSolution() {
		return solution;
	}

	public void setSolution(Solution solution) {
		this.solution = solution;
	}

	public int getLabel() {
		return label_;
	}

	public void setLabel(int label) {
		this.label_ = label;
	}

} // Solution
